﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

namespace Love.Awesome.SimpleTiledImplementation
{

    public class BakeResult
    {
        public Canvas Map;
        public Dictionary<int, Node> Coords;
    }

    public enum MapSortEnum
    {
        Default, ID, Size
    }

    public class Node
    {
        public int W;
        public int H;
        public int X;
        public int Y;

        public Node Left;
        public Node Right;
        public int ID;
        internal Image Image;
        internal IDictionary<int, string> Coords;

        public Node()
        {

        }

        public Node(int x, int y, int w, int h)
        {
            X = x; Y = y; W = w; H = h;
        }
    }

    ///<summary>地图集相关处理。</summary>
    public static class AtlasUtils
    {
        public static Node CreateNode(int x, int y, int w, int h)
        {
            return new Node(x, y, w, h);
        }

        public static int NextPow2(int n)
        {
            var res = 1;
            while (res <= n)
            {
                res = res * 2;
            }
            return res;
        }

        public static IList<ImageInfo> LoadImgs(IEnumerable<string> files, MapSortEnum sortType)
        {
            var images = new List<ImageInfo>();
            var fileArray = files.ToArray();

            foreach (var file in files)
            {
                var imageInfo = new ImageInfo();

                var newImage = Love.Graphics.NewImage(file);
                imageInfo.Img = newImage;
                imageInfo.W = newImage.GetWidth();
                imageInfo.H = newImage.GetHeight();
                imageInfo.Area = imageInfo.W * imageInfo.H;

                images.Add(imageInfo);
            }

            if (sortType == MapSortEnum.ID || sortType == MapSortEnum.Size)
            {
                images.Sort(ImageInfoSizeComparer.Instance);
            }

            return images;
        }

        ///<summary>通过递归创建树结构。</summary>
        public static Node Add(Node root, int id, int w, int h)
        {
            if (root.Left != null || root.Right != null)
            {
                if (root.Left != null)
                {
                    var node = Add(root.Left, id, w, h);
                    if (node != null) return node;
                }

                if (root.Right != null)
                {
                    var node = Add(root.Right, id, w, h);
                    if (node != null) return node;
                }

                return null;
            }

            if (w > root.W || h > root.H)
            {
                return null;
            }

            var tw = root.W - w;
            var th = root.H - h;

            if (tw <= th)
            {
                root.Left = CreateNode(root.X + w, root.Y, tw, h);
                root.Right = CreateNode(root.X, root.Y + h, root.W, th);
            }
            else
            {
                root.Left = CreateNode(root.X, root.Y + h, w, th);
                root.Right = CreateNode(root.X + w, root.Y, tw, root.H);
            }

            root.W = w;
            root.H = h;
            root.ID = id;

            return root;

        }

        public static Dictionary<int, Node> Unmap(Node root)
        {
            if (root == null) return new Dictionary<int, Node>();

            var tree = new Dictionary<int, Node>();

            if (tree.ContainsKey(root.ID) == false)
            {
                tree.Add(root.ID, new Node() { X = root.X, Y = root.Y });
            }

            var left = Unmap(root.Left);
            var right = Unmap(root.Right);

            foreach (var pair in left)
            {
                var id = pair.Key;
                var node = pair.Value;
                tree.Add(id, new Node() { X = node.X, Y = node.Y });
            }

            foreach (var pair in right)
            {
                var id = pair.Key;
                var node = pair.Value;
                tree.Add(id, new Node() { X = node.X, Y = node.Y });
            }

            return tree;

        }

        public static BakeResult Bake(IEnumerable<string> files, MapSortEnum sortType, IList<string> ids, bool pow2)
        {
            var images = LoadImgs(files, sortType);
            Node root = null;
            var w = 0;
            var h = 0;

            if (pow2)
            {
                // 我不明白为什么要对 1 取余。
                if (w % 1 == 0) w = NextPow2(w);
                if (h % 1 == 0) h = NextPow2(h);
            }

            while (true)
            {
                Node node = null;
                root = CreateNode(0, 0, w, h);

                for (int i = 0; i < images.Count; i++)
                {
                    node = Add(root, i, (int)images[i].W, (int)images[i].H);
                    if (node == null) break;
                }

                if (node == null)
                {
                    if (h <= w)
                    {
                        if (pow2) { h = h * 2; } else { h = h + 1; }
                    }
                    else
                    {
                        if (pow2) { w = w * 2; } else { w = w + 1; }
                    }
                }
                else
                {
                    break;
                }
            }

            var limitsTextureSize = Love.Graphics.GetSystemLimits(Love.Graphics.SystemLimit.TextureSize);
            if (w > limitsTextureSize || h > limitsTextureSize)
            {
                throw new Exception("Resulting texture is too large for this system");
            }
            var coords = Unmap(root);
            var map = Love.Graphics.NewCanvas(w, h);
            Love.Graphics.SetCanvas(map);
            // Love.Graphics.Clear();

            for (int i = 0; i < images.Count; i++)
            {
                Love.Graphics.Draw(images[i].Img, coords[i].X, coords[i].Y);
                if (ids != null) coords[i].ID = images[i].ID;
            }

            Love.Graphics.SetCanvas();

            if (sortType == MapSortEnum.ID)
            {
                // 列表的排序，使用 lua 实现但是在 C# 中因为机制不一样，较难实现。
                // table.sort( coords, function( a, b ) return ( a.id < b.id ) end )
            }


            return new BakeResult() { Map = map, Coords = coords };
        }


      
        ///<summary>创建地图集。</summary>
        public static Node Atlas(IEnumerable<string> files, MapSortEnum sortType, IList<int> ids, bool pow2 = false)
        {
            return null;
        }
    }
}
